#pragma once

#include <iostream>
#include <sstream>
#include <algorithm>
#include <string>
#include <vector>
#include <unordered_map>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include "Util.hpp"
#include "Log.hpp"

const char* SEP=": ";
const char* WWWROOT="wwwroot";
const char* HOME_PAGE="index.html";
const char* LINE_END="\r\n";
const char* VERSION="HTTP/1.0";
const char* PAGE_404="404.html";

const int OK=200;
const int BAD_REQUEST=400;
const int NOT_FOUND=404; 
const int SERVER_ERROR=500;

static std::string Code2Desc(int code)
{
  std::string desc;
  switch(code)
  {
    case 200:
      desc="OK";
      break;
    case 404:
      desc="Not Found";
      break;
    default :
      break;
  }

  return desc;
}

static std::string Suffix2Desc(const std::string& suffix)
{
  static std::unordered_map<std::string,std::string> suffix2desc={
  {".html","text/html"},
  {".css","text/css"},
  {".js","application/javascript"},
  {".jpg","application/x-jpg"},
  {".xml","application/xml"}
  };

  auto iter=suffix2desc.find(suffix);
  if(iter!=suffix2desc.end())
  {
    return iter->second;
  }

  return "text/html";
}

class HttpRequest
{
  public:
    std::string request_line;
    std::vector<std::string> request_header;
    std::string request_bank;
    std::string request_body;

    //解析请求
    std::string method;
    std::string uri;
    std::string version;

    std::unordered_map<std::string,std::string> header_kv;
    int content_length;
    std::string path;
    std::string suffix;
    std::string query_string;

    bool cgi;
    int size;

  public:
    HttpRequest()
      :content_length(0)
       ,cgi(false)
    {}

    ~HttpRequest()=default;
};

class HttpResponse
{
  public:
    std::string response_line;
    std::vector<std::string> response_header;
    std::string response_bank;
    std::string response_body;

    int status_code;
    int fd;

  public:
    HttpResponse()
      :response_bank(LINE_END)
      ,status_code(OK)
      ,fd(-1)
    {}

    ~HttpResponse()=default;

};

//读取请求，分析请求，构建响应
//IO通信
class EndPoint
{
  private:
    int _sock;
    HttpRequest http_request;
    HttpResponse http_response;
    bool stop;

  private:
    //----------------------------请求处理系列------------------------------//
    bool RecvHttpRequestLine()
    {
      auto& line=http_request.request_line;
      ssize_t s=Util::ReadLine(_sock,line);
      if(s>0)
      {
        line.resize(line.size()-1);//不以\n结尾
        LOG(INFO,line);
      }
      else
      {
        stop=true;
      }

      return stop;
    }

    bool RecvHttpRequestHeader()
    {
      auto& header=http_request.request_header;
      std::string line;
      while(true)
      {
        line.clear();
        size_t s=Util::ReadLine(_sock,line);
        if(s<=0)
        {
          stop=true;
          break;
        }
        
        if(line=="\n")
        {
          http_request.request_bank="\n";
          break;
        }
        line.resize(line.size()-1);
        header.push_back(line);
        LOG(INFO,line);
      }

      return stop;
    }

    void ParseHttpRequestLine()
    {
      auto& method=http_request.method;
      auto& uri=http_request.uri;
      auto& version=http_request.version;
      auto& line=http_request.request_line;

      //利用stringstream以空格为分割符将内容进行分割
      std::stringstream ss(line);
      ss>>method>>uri>>version;

      //为了统一，将方法全部转化为大写
      std::transform(method.begin(),method.end(),method.begin(),::toupper);
    }

    void ParseHttpRequestHeader()
    {
      std::string key;
      std::string value;
      auto& header=http_request.request_header;
      auto& header_kv=http_request.header_kv;

      for(auto& str:header)
      {
        if(Util::CutString(str,key,value,SEP))
        {
          header_kv.insert({key,value});
        }
      }
    }

    bool IsRecvHttpRequestBody()
    {
      auto& method=http_request.method;
      auto& header_kv=http_request.header_kv;
      auto& content_length=http_request.content_length;

      if("POST"==method)
      {
        auto iter=header_kv.find("Content-Length");
        if(iter!=header_kv.end())
        {
          LOG(INFO,"Post Method,Content-Length: "+iter->second);
          content_length=atoi(iter->second.c_str());
          return true;
        }
      }

      return false;
    }

    bool RecvHttpRequestBody()
    {
      auto& content_length=http_request.content_length;
      auto& request_body=http_request.request_body;

      if(IsRecvHttpRequestBody())
      {
        int size=content_length;
        char ch=0;
        while(size)
        {
          ssize_t s=recv(_sock,&ch,1,0);
          if(s>0)
          {
            request_body+=ch;
            size--;
          }
          else
          {
            stop=true;
            break;
          }
        }

        LOG(INFO,request_body);
      }

      return stop;
    }
    //------------------------------------请求处理系列--------------------------------//
    
    int ProcessCgi()
    {
      LOG(INFO,"process cgi method");
      
      int code=OK;
      auto& method=http_request.method;
      auto& query_string=http_request.query_string;//GET
      auto& request_body=http_request.request_body;//POST
      auto& bin=http_request.path;//一定存在
      auto& content_length=http_request.content_length;
      auto& response_body=http_response.response_body;

      std::string query_string_env;
      std::string method_env;
      std::string content_length_env;

      //站在父进程的角度
      int inpipe[2];
      int outpipe[2];

      //创建两个匿名管道
     if(pipe(inpipe)!=0) 
     {
       LOG(ERROR,"pipe input fail");
       code=SERVER_ERROR;
       return code;
     }
     if(pipe(outpipe)!=0)
     {
       LOG(ERROR,"pipe output fail");
       code=SERVER_ERROR;
       return code;
     }

     //创建进程
      pid_t pid=fork();
      if(pid==0)//子进程
      {
        //inpipe[1]->写入
        //outpipe[0]->读取
        close(inpipe[0]);
        close(outpipe[1]);

        //写入对应的条件变量
        method_env+="METHOD=";
        method_env+=method;
        putenv((char*)method_env.c_str());

        if(method=="GET")
        {
          query_string_env+="QUERY_STRING=";
          query_string_env+=query_string;
          putenv((char*)query_string_env.c_str());
          LOG(INFO,"Get Method,Add Query_String Env");
        }
        else if(method=="POST")
        {
          content_length_env+="CONTENT_LENGTH=";
          content_length_env+=std::to_string(content_length);
          putenv((char*)content_length_env.c_str());
          LOG(INFO,"Post Method,Add Content_Length Env");
        }
        else
        {
          //do nothing
        }

        //重定向
        dup2(outpipe[0],0);
        dup2(inpipe[1],1);

        //程序替换
        execl(bin.c_str(),bin.c_str(),nullptr);
        exit(1);
      }
      else if(pid>0)//父进程
      {
        //父进程
        //inpipe[0]->读取
        //outpipe[1]->写入
        close(inpipe[1]);
        close(outpipe[0]);

        if(method=="POST")
        {
          const char* start=request_body.c_str();
          //std::cout<<request_body<<std::endl;
          int size=0;
          int total=0;
          //std::cout<<content_length<<std::endl;
          while((total<content_length)&&(size=write(outpipe[1],start+total,request_body.size()-total))>0)
          {
            total+=size;
          }
        }

        char ch=0;
        while(read(inpipe[0],&ch,1)>0)
        {
          response_body+=ch;
        }
        std::cout<<response_body<<std::endl;


        int status=0;
        pid_t ret=waitpid(pid,&status,0);
        if(ret==pid)
        {
          if(WIFEXITED(status))//查看退出信号
          {
            if(WEXITSTATUS(status)==0)//查看退出码
            {
              code=OK;
            }
            else
            {
              code=BAD_REQUEST;
            }
          }
          else
          {
            code=SERVER_ERROR;
          }
        }


        close(inpipe[0]);
        close(outpipe[1]);
      }
      else
      {
        //进程创建失败
        LOG(ERROR,"fork fail");
        code=BAD_REQUEST;
        return code;
      }
      return code;
    }


    int ProcessNonCgi()
    {
      //只需打开文件
      auto& fd=http_response.fd;
      auto& path=http_request.path;

      fd=open(path.c_str(),O_RDONLY);
      if(fd>=0)
      {
        LOG(INFO,path+" open success");
        return OK;
      }

      return BAD_REQUEST;
    }

    void BuildOkResponse()
    {
      auto& suffix=http_request.suffix;
      auto& size=http_request.size;;
      auto& cgi=http_request.cgi;
      auto& response_body=http_response.response_body;
      auto& response_header=http_response.response_header;

      std::string line="Content-Type: ";
      line+=Suffix2Desc(suffix);
      line+=LINE_END;
      response_header.push_back(line);
      
      line="Content-Length: ";
      if(cgi)
      {
        line+=std::to_string(response_body.size());
      }
      else
      {
        line+=std::to_string(size);
      }
      line+=LINE_END;
      response_header.push_back(line);
    }

    void HandlerError(std::string page) 
    {
      auto& cgi=http_request.cgi;
      auto& response_header=http_response.response_header;
      auto& size=http_request.size;
      auto& fd=http_response.fd;

      cgi=false;//对于非CGI操作而出错的情况处理

      fd=open(page.c_str(),O_RDONLY);
      if(fd>=0)
      {
        struct stat st;
        stat(page.c_str(),&st);
        size=st.st_size;

        std::string line="Content-Type: text/html";
        line+=LINE_END;
        response_header.push_back(line);

        line="Content-Length: ";
        line+=std::to_string(size);
        line+=LINE_END;
        response_header.push_back(line);
      }
    }

    void BuildHttpResponseHelper()
    {
      auto& response_line=http_response.response_line;
      auto& code=http_response.status_code;

      //构建响应行
      response_line+=VERSION;
      response_line+=" ";
      response_line+=std::to_string(code);
      response_line+=" ";
      response_line+=Code2Desc(code);
      response_line+=LINE_END;

      //构建响应正文，可能包括响应属性
      std::string path=WWWROOT;
      path+="/";
      switch(code)
      {
        case OK:
          BuildOkResponse();
          break;
        case NOT_FOUND:
          path+=PAGE_404;
          HandlerError(path);
          break;
        case BAD_REQUEST: 
          path+=PAGE_404;
          HandlerError(path);
          break;
        case SERVER_ERROR:
          path+=PAGE_404;
          HandlerError(path);
          break;
        default :
          break;
      }
    }
    

  public:
    EndPoint(int sock)
      :_sock(sock)
      ,stop(false)//终止读取出错
    {}

    bool IsStop()
    {
      return stop;
    }

    ~EndPoint()
    {
      close(_sock);
    }

    void RecvHttpRequest()
    {
      //获取请求行
      //获取请求属性
      //解析请求行
      //解析请求属性
      //获取请求正文
      if((!RecvHttpRequestLine())&&(!RecvHttpRequestHeader()))
      {
        ParseHttpRequestLine();
        ParseHttpRequestHeader();
        RecvHttpRequestBody();
      }
    }

    void BuildHttpResopnse()
    {
      //构建响应行
      //构建响应属性
      //构建响应正文
      //请求已经有了，直接构建响应
      auto& method=http_request.method;
      auto& code=http_response.status_code;
      auto& uri=http_request.uri;
      auto& path=http_request.path;
      auto& query_string=http_request.query_string;
      auto& cgi=http_request.cgi;
      auto& size=http_request.size;
      auto& suffix=http_request.suffix;

      std::string _path;
      struct stat st;
      size_t found=0;

      if(method!="GET"&&method!="POST")
      {
        //非法请求
        std::cout<<"method: "<<method<<std::endl;
        LOG(WARNING,"method is not right");
        code=BAD_REQUEST;  
        goto END;
      }
      else if("GET"==method)
      {
        
        size_t pos=uri.find("?");
        if(pos!=std::string::npos)
        {
          //GET提交参数
          Util::CutString(uri,path,query_string,"?");
          cgi=true;
        }
        else
        {
          //GET单纯获取资源
          path=uri;
        }
      }
      else if("POST"==method)
      {
        //提交参数
        cgi=true;
        path=uri;
      }
      else 
      {
        //do nothing
      }
      
      //构建合理的访问路径
      _path=path;
      path=WWWROOT;
      path+=_path;
      if(path.back()=='/')
      {
        path+=HOME_PAGE;
      }

      //判断路径
      if(stat(path.c_str(),&st)==0)
      {
        //存在
        //判断请求的是否为目录或者是可执行程序
        if(S_ISDIR(st.st_mode))//访问的路径为目录
        {
          path+="/";
          path+=HOME_PAGE;
          stat(path.c_str(),&st);
        }
        if((st.st_mode&S_IXUSR)||(st.st_mode&S_IXGRP)||(st.st_mode&S_IXOTH))//访问的路径为可执行程序
        {
          cgi=true;
        }

        size=st.st_size;
      }
      else
      {
        //不存在
        LOG(WARNING,path+" Not Found");
        code=NOT_FOUND;
        goto END;
      }
      
      //填充后缀
      found=path.rfind(".");
      if(found==std::string::npos)
      {
        suffix=".html";
      }
      else
      {
        suffix=path.substr(found);
      }

      if(cgi)
      {
        code=ProcessCgi();
      }
      else
      {
        //目标网页一定存在
        code=ProcessNonCgi();
      }

END:
      BuildHttpResponseHelper();
    }

    void SendHttpResopnse()
    {
      auto& response_line=http_response.response_line;
      auto& response_header=http_response.response_header;
      auto& response_bank=http_response.response_bank;
      auto& response_body=http_response.response_body;
      auto& cgi=http_request.cgi;
      auto& fd=http_response.fd;

      send(_sock,response_line.c_str(),response_line.size(),0);
      for(auto& str:response_header)
      {
        send(_sock,str.c_str(),str.size(),0);
      }
      send(_sock,response_bank.c_str(),response_bank.size(),0);
      if(cgi) 
      {
        const char* start=response_body.c_str();
        size_t size=0;
        size_t total=0;
        while((total!=response_body.size())&&(size=send(_sock,start+total,response_body.size()-total,0))>0)
        {
          total+=size;
        }
      }
      else
      {
        sendfile(_sock,fd,nullptr,http_request.size);
        close(fd);
      }
    }
};

class CallBack
{
  public:
    CallBack()=default;
    ~CallBack()=default;

    void operator()(int sock)
    {
      HandlerRequest(sock);
    }

    void HandlerRequest(int sock)
    {
      LOG(INFO,"Handler Request Begin");

      EndPoint* ep=new EndPoint(sock);
      ep->RecvHttpRequest();//获取请求
      if(!ep->IsStop())
      {
        LOG(INFO,"Recv Success,Begin Build And Send");
        ep->BuildHttpResopnse();//构建响应
        ep->SendHttpResopnse();
      }
      else
      {
        LOG(WARNING,"Recv fail,Stop Build And Send");
      }
      delete ep;
    }
};
